@@ -19,20 +19,17 @@ my $build = Local::Module::Build->new(
# extra_compiler_flags => "-g -Wall -ansi -pedantic -Wno-long-long -Wextra -Wdeclaration-after-statement -Wendif-labels -Wconversion",
- PL_files => {
- 'munge_config' => 'y2038/time64_config.h'
- },
-
- configure_requires => {
- 'Module::Build' => '0.2808',
- },
-
build_requires => {
- 'Module::Build' => '0.2808',
+ 'Module::Build' => '0.36',
'Test::Warn' => '0.11',
'Test::Exception' => '0.27',
- 'Test::More' => 0.62,
+ 'Test::More' => 0.82,
'ExtUtils::CBuilder' => 0.24,
+ 'JSON' => 2.17,
+ },
+
+ configure_requires => {
+ 'Module::Build' => '0.36'
},
requires => {
@@ -1,3 +1,65 @@
+20100218 Thu Feb 18 12:29:49 PST 2010
+ * No changes since the last alpha
+
+ Summary of Changes Since The Last Stable
+
+ Improvements
+ * System mktime() is now probed giving more accurate
+ mktime() on 64 bit systems.
+
+ Test
+ * Test fixes for 64 bit machines, OS X, BSD, people in the UK
+
+ Build
+ * Build fixes Versions of Visual C++ lacking long long
+ * Build fixes for Strawberry and any system with a time.h
+ limit of 0.
+ * Build fixes for slightly out of date Module::Build
+
+
+20100216.1421_04 Tue Feb 16 14:22:08 PST 2010
+ Build
+ * Switch dependency on JSON::XS to just JSON to eliminate
+ Lehmann-ware dependency.
+ * The test for the limits of the time.h did not work for
+ finding functions.
+ * Work around bug in OS X 10.6 where gmtime() would be wrong
+ before -70546986201600.
+
+ Test
+ * Add some more fuzz around the future localtime() tests.
+
+
+20100214.1823_03 Sun Feb 14 18:23:22 PST 2010
+ Build
+ * Bump up the minimum Module::Build release to fix a bug where
+ Build would not see inc/ and thus couldn't find
+ Local::Module::Build.
+
+
+20100213.0504_02 Sat Feb 13 05:04:39 PST 2010
+ Build
+ * The check for the minimum time and date was broken and would only
+ do a single check.
+
+
+20100213.0000_01 Sat Feb 13 04:32:59 PST 2010
+ Improvements
+ * mktime() and timelocal() will now make better use of the system
+ functions giving better accuracy.
+
+ Build
+ * Had too old a version of Module::Build listed as the requirement.
+ * The limit check program was ignoring a limit of 0. Duh.
+ * Fix so the check_max program is not always re-run on Cygwin.
+
+ Tests
+ * More localtime() tests to track down the 64 bit issues
+ * Failures on 64 bit systems, especially in the UK, should
+ be fixed
+ * More sanity tests for distant dates
+
+
20081111 Tue Nov 11 15:36:27 PST 2008
Build Improvements
* Moved building the program to check the limits of time.h to the code
@@ -17,4 +17,4 @@ typemap
y2038/time64.c
y2038/time64.h
y2038/time64_config.h.in
-SIGNATURE Added here by Module::Build
+y2038/time64_limits.h.in
@@ -44,6 +44,8 @@
check_max$
lib/Time/y2038\.c
y2038/time64_config.h$
+y2038/time64_limits.h$
# Avoid our own dists
^Time-y2038-
+^MYMETA.yml$
@@ -1,30 +1,31 @@
---
-name: Time-y2038
-version: 20081111
+abstract: "Versions of Perl's time functions which work beyond 2038"
author:
- 'Michael G Schwern <schwern@pobox.com>'
-abstract: Versions of Perl's time functions which work beyond 2038
-license: perl
-resources:
- license: http://dev.perl.org/licenses/
-configure_requires:
- Module::Build: 0.2808
-requires:
- perl: 5.6.1
build_requires:
ExtUtils::CBuilder: 0.24
- Module::Build: 0.2808
+ JSON: 2.17
+ Module::Build: 0.36
Test::Exception: 0.27
- Test::More: 0.62
+ Test::More: 0.82
Test::Warn: 0.11
+configure_requires:
+ Module::Build: 0.36
+generated_by: 'Module::Build version 0.3601'
+license: perl
+meta-spec:
+ url: http://module-build.sourceforge.net/META-spec-v1.4.html
+ version: 1.4
+name: Time-y2038
provides:
Time::y2038:
file: lib/Time/y2038.pm
- version: 20081111
+ version: 20100218
Time::y2038::Everywhere:
file: lib/Time/y2038/Everywhere.pm
- version: 20081111
-generated_by: Module::Build version 0.3
-meta-spec:
- url: http://module-build.sourceforge.net/META-spec-v1.2.html
- version: 1.2
+ version: 20100218
+requires:
+ perl: v5.6.1
+resources:
+ license: http://dev.perl.org/licenses/
+version: 20100218
@@ -1,42 +0,0 @@
-This file contains message digests of all files listed in MANIFEST,
-signed via the Module::Signature module, version 0.55.
-
-To verify the content in this distribution, first make sure you have
-Module::Signature installed, then type:
-
- % cpansign -v
-
-It will check each file's integrity, as well as the signature's
-validity. If "==> Signature verified OK! <==" is not displayed,
-the distribution may already have been compromised, and you should
-not run its Makefile.PL or Build.PL.
-
------BEGIN PGP SIGNED MESSAGE-----
-Hash: SHA1
-
-SHA1 86f971cde11d55c132b442933c01634cab54586b Build.PL
-SHA1 255de6249cf6df584cb3d0efb8681407e12e844b Changes
-SHA1 fb110d0098fa623a2d6566e86a6d630110f60d23 INSTALL
-SHA1 02f5e7b8483d8f395dc26aac98d417a6596263b7 MANIFEST
-SHA1 9698a8c937c13d598301a07120b1472d163390f6 MANIFEST.SKIP
-SHA1 9b7b1358f90cf465521caa5b7d3e0cd603024362 META.yml
-SHA1 394150edea65b31e2054df4bac62eb301fa90507 check_max.c
-SHA1 54ec76512053479e4c6e96e7880d3d22a99ddee8 inc/Local/Module/Build.pm
-SHA1 80426fe1294d7ca50471b6fa931a102030dd6ae4 lib/Time/y2038.pm
-SHA1 666e25e498a3bd6bdb9bcab3f9e5b8812e4567f3 lib/Time/y2038.xs
-SHA1 14090ec73b8f16dd06c58b0282f049b0a66c60ea lib/Time/y2038/Everywhere.pm
-SHA1 86c1c0b6f58666b6ac2247312cf459b903b75dd5 munge_config
-SHA1 2997ee7e8bed8f51793ff239c094bd1838d12e06 t/everywhere.t
-SHA1 a9dd5089dd4bfae40c9cfa72d2bcb7be7f46591a t/time.t
-SHA1 0f7a96de5a04d9326e963cb480a50ebf783c5b2c t/timegm.t
-SHA1 a915486e0da8713dead77ca21d041c8ae6b6676e typemap
-SHA1 96834fb722bb467065888454275a21c36fb7c1b3 y2038/time64.c
-SHA1 c0dfb7f4e4dca8eaa3a28bbe0b680ebd36eaf722 y2038/time64.h
-SHA1 7761df7832a8e3df7976d9a278d684ea37b2bdc1 y2038/time64_config.h.in
------BEGIN PGP SIGNATURE-----
-Version: GnuPG v1.4.9 (Darwin)
-
-iEYEARECAAYFAkkaF60ACgkQWMohlhD1QycmZwCgk/u5RwRMO9GqzMqaRNQjoPEJ
-tRwAoMZsXozLnb9rKEfsc0xNH5H9Mlny
-=pdkF
------END PGP SIGNATURE-----
@@ -1,8 +1,11 @@
/* A little program to test the limits of your system's time functions */
+#include "time64_config.h"
#include <time.h>
#include <stdio.h>
#include <math.h>
+#include <stdlib.h>
+#include <string.h>
struct tm Test_TM;
@@ -12,13 +15,25 @@ time_t Time_Min;
time_t Time_Zero = 0;
+char *dump_date(const struct tm *date) {
+ char *dump = malloc(80 * sizeof(char));
+ sprintf(
+ dump,
+ "{ %d, %d, %d, %d, %d, %d }",
+ date->tm_sec, date->tm_min, date->tm_hour, date->tm_mday, date->tm_mon, date->tm_year
+ );
+
+ return dump;
+}
+
+
/* Visual C++ 2008's difftime() can't do negative times */
double my_difftime(time_t left, time_t right) {
- double diff = (double)left - (double)right;
- return diff;
+ double diff = (double)left - (double)right;
+ return diff;
}
-void check_date_max( struct tm * (*date_func)(const time_t *), char *func_name ) {
+time_t check_date_max( struct tm * (*date_func)(const time_t *), const char *func_name ) {
struct tm *date;
time_t time = Time_Max;
time_t good_time = 0;
@@ -43,11 +58,11 @@ void check_date_max( struct tm * (*date_func)(const time_t *), char *func_name )
}
} while(time_change > 0 && good_time < Time_Max);
- printf("%s_max %.0f\n", func_name, my_difftime(good_time, Time_Zero));
+ return(good_time);
}
-void check_date_min( struct tm * (*date_func)(const time_t *), char *func_name ) {
+time_t check_date_min( struct tm * (*date_func)(const time_t *), const char *func_name ) {
struct tm *date;
time_t time = Time_Min;
time_t good_time = 0;
@@ -60,7 +75,7 @@ void check_date_min( struct tm * (*date_func)(const time_t *), char *func_name )
time_change /= 2;
- /* gmtime() broke or tm_year overflowed or time_t overflowed */
+ /* date_func() broke or tm_year overflowed or time_t overflowed */
if(date == NULL || date->tm_year > 70 || time > good_time) {
printf(" failed\n");
time -= time_change;
@@ -70,9 +85,79 @@ void check_date_min( struct tm * (*date_func)(const time_t *), char *func_name )
good_time = time;
time += time_change;
}
- } while((time_change > 0) && (good_time > Time_Min));
+ } while((time_change < 0) && (good_time > Time_Min));
+
+ return(good_time);
+}
+
+
+struct tm * check_to_time_max( time_t (*to_time)(struct tm *), const char *func_name,
+ struct tm * (*to_date)(const time_t *) )
+{
+ time_t round_trip;
+ time_t time = Time_Max;
+ time_t good_time = 0;
+ struct tm *date;
+ struct tm *good_date = malloc(sizeof(struct tm));
+ time_t time_change = Time_Max;
+
+ /* Binary search for the exact failure point */
+ do {
+ printf("# Trying %s(%.0f) max...", func_name, my_difftime(time, Time_Zero));
+ date = (*to_date)(&time);
+ round_trip = (*to_time)(date);
+
+ time_change /= 2;
+
+ /* date_func() broke or tm_year overflowed or time_t overflowed */
+ if(time != round_trip) {
+ printf(" failed\n");
+ time -= time_change;
+ }
+ else {
+ printf(" success\n");
+ good_time = time;
+ memcpy(good_date, date, sizeof(struct tm));
+ time += time_change;
+ }
+ } while(time_change > 0 && good_time < Time_Max);
+
+ return(good_date);
+}
+
+
+struct tm * check_to_time_min( time_t (*to_time)(struct tm *), const char *func_name,
+ struct tm * (*to_date)(const time_t *) )
+{
+ time_t round_trip;
+ time_t time = Time_Min;
+ time_t good_time = 0;
+ struct tm *date;
+ struct tm *good_date = malloc(sizeof(struct tm));
+ time_t time_change = Time_Min;
+
+ /* Binary search for the exact failure point */
+ do {
+ printf("# Trying %s(%.0f) min...", func_name, my_difftime(time, Time_Zero));
+ date = (*to_date)(&time);
+ round_trip = (*to_time)(date);
+
+ time_change /= 2;
+
+ /* date_func() broke or tm_year overflowed or time_t overflowed */
+ if(time != round_trip) {
+ printf(" failed\n");
+ time -= time_change;
+ }
+ else {
+ printf(" success\n");
+ good_time = time;
+ memcpy(good_date, date, sizeof(struct tm));
+ time += time_change;
+ }
+ } while((time_change < 0) && (good_time > Time_Min));
- printf("%s_min %.0f\n", func_name, my_difftime(good_time, Time_Zero));
+ return(good_date);
}
@@ -80,7 +165,8 @@ void guess_time_limits_from_types(void) {
if( sizeof(time_t) == 4 ) {
/* y2038 bug, out to 2**31-1 */
Time_Max = 2147483647;
- Time_Min = -2147483648;
+ Time_Min = -2147483647 - 1; /* "C90 doesn't have negative constants, only
+ positive ones that have been negated." */
}
else if( sizeof(time_t) >= 8 ) {
/* The compiler might warn about overflowing in the assignments
@@ -101,12 +187,95 @@ void guess_time_limits_from_types(void) {
}
}
+
+/* Dump a tm struct as a json fragment */
+char * tm_as_json(const struct tm* date) {
+ char *date_json = malloc(sizeof(char) * 512);
+#ifdef HAS_TM_TM_ZONE
+ char zone_json[32];
+#endif
+#ifdef HAS_TM_TM_GMTOFF
+ char gmtoff_json[32];
+#endif
+
+ sprintf(date_json,
+ "\"tm_sec\": %d, \"tm_min\": %d, \"tm_hour\": %d, \"tm_mday\": %d, \"tm_mon\": %d, \"tm_year\": %d, \"tm_wday\": %d, \"tm_yday\": %d, \"tm_isdst\": %d",
+ date->tm_sec, date->tm_min, date->tm_hour, date->tm_mday,
+ date->tm_mon, date->tm_year, date->tm_wday, date->tm_yday, date->tm_isdst
+ );
+
+#ifdef HAS_TM_TM_ZONE
+ sprintf(zone_json, ", \"tm_zone\": \"%s\"", date->tm_zone);
+ strcat(date_json, zone_json);
+#endif
+#ifdef HAS_TM_TM_GMTOFF
+ sprintf(gmtoff_json, ", \"tm_gmtoff\": %ld", date->tm_gmtoff);
+ strcat(date_json, gmtoff_json);
+#endif
+
+ return date_json;
+}
+
+
int main(void) {
+ time_t gmtime_max;
+ time_t gmtime_min;
+ time_t localtime_max;
+ time_t localtime_min;
+#ifdef HAS_TIMEGM
+ struct tm* timegm_max;
+ struct tm* timegm_min;
+#endif
+ struct tm* mktime_max;
+ struct tm* mktime_min;
+
guess_time_limits_from_types();
- check_date_max(gmtime, "gmtime");
- check_date_max(localtime, "localtime");
- check_date_min(gmtime, "gmtime");
- check_date_min(localtime, "localtime");
+
+ gmtime_max = check_date_max(gmtime, "gmtime");
+ gmtime_min = check_date_min(gmtime, "gmtime");
+
+ localtime_max = check_date_max(localtime, "localtime");
+ localtime_min = check_date_min(localtime, "localtime");
+
+#ifdef HAS_TIMEGM
+ Time_Max = gmtime_max;
+ Time_Min = gmtime_min;
+ timegm_max = check_to_time_max(timegm, "timegm", gmtime);
+ timegm_min = check_to_time_min(timegm, "timegm", gmtime);
+#endif
+
+ Time_Max = localtime_max;
+ Time_Min = localtime_min;
+ mktime_max = check_to_time_max(mktime, "mktime", localtime);
+ mktime_min = check_to_time_min(mktime, "mktime", localtime);
+
+ printf("# system time.h limits, as JSON\n");
+ printf("{\n");
+
+ printf(" \"gmtime\": { \"max\": %.0f, \"min\": %0.f },\n",
+ my_difftime(gmtime_max, Time_Zero),
+ my_difftime(gmtime_min, Time_Zero)
+ );
+
+ printf(" \"localtime\": { \"max\": %.0f, \"min\": %0.f },\n",
+ my_difftime(localtime_max, Time_Zero),
+ my_difftime(localtime_min, Time_Zero)
+ );
+
+ printf(" \"mktime\": {\n");
+ printf(" \"max\": { %s },\n", tm_as_json(mktime_max));
+ printf(" \"min\": { %s }\n", tm_as_json(mktime_min));
+ printf(" }\n");
+
+#ifdef HAS_TIMEGM
+ printf(" ,\n");
+ printf(" \"timegm\": {\n");
+ printf(" \"max\": { %s },\n", tm_as_json(timegm_max));
+ printf(" \"min\": { %s }\n", tm_as_json(timegm_min));
+ printf(" }\n");
+#endif
+
+ printf("}\n");
return 0;
}
@@ -3,57 +3,201 @@ package Local::Module::Build;
use strict;
use base qw(Module::Build);
+use ExtUtils::CBuilder;
+use JSON;
+
+sub is_osx_106 {
+ return 0 unless $^O eq 'darwin';
+ my $version = `sw_vers -productVersion`;
+ return $version =~ m{^10\.6\.};
+}
+
+sub probe_system_time {
+ my $self = shift;
+ $self->note_time_capabilities;
+ $self->munge("y2038/time64_config.h.in" => "y2038/time64_config.h");
+ $self->note_time_limits;
+ $self->munge("y2038/time64_limits.h.in" => "y2038/time64_limits.h");
+}
+
+sub munge {
+ my $self = shift;
+ my($src, $dest) = @_;
+
+ return if $self->up_to_date(["munge_config",$src] => [$dest]);
+ system $^X, "munge_config", $src, $dest;
+ $self->add_to_cleanup($dest);
+}
+
+sub note_time_capabilities {
+ my $self = shift;
+
+ return if $self->up_to_date(["Build", "y2038/time64_limits.h.in"], ["y2038/time64_limits.h"]);
+ my %tests = (
+ HAS_TIMEGM => <<'END',
+ struct tm *date;
+ time_t zero;
+
+ date = localtime(&zero);
+ zero = timegm(date);
+END
+
+ HAS_GMTIME_R => <<'END',
+ struct tm date;
+ time_t zero = 0;
+ (void)gmtime_r(&zero, &date);
+END
+
+ HAS_LOCALTIME_R => <<'END',
+ struct tm date;
+ time_t zero = 0;
+ (void)localtime_r(&zero, &date);
+END
+
+ HAS_TM_TM_GMTOFF => <<'END',
+ struct tm *date;
+ time_t zero;
+ int offset;
+
+ date = gmtime(&zero);
+ offset = date->tm_gmtoff;
+END
+
+ HAS_TM_TM_ZONE => <<'END',
+ struct tm *date;
+ time_t zero;
+ char *zone;
+
+ date = gmtime(&zero);
+ zone = date->tm_zone;
+END
+ );
+
+ warn "Probing time.h capabilities.\n";
+ warn "You may see some C errors, that's ok.\n";
+ my $cb = ExtUtils::CBuilder->new( quiet => 1 );
+ for my $test (keys %tests) {
+ my $code = $tests{$test};
+
+ $code = <<END;
+#include <time.h>
+
+int main() {
+$code
+
+ return 0;
+}
+END
+
+ open my $fh, ">", "try.c" or die "Can't write try.c: $!";
+ print $fh $code;
+ close $fh;
+
+ my $exe = eval {
+ # Compile AND link to force undefined symbols to error
+ my $obj = $cb->compile(source => "try.c");
+ my $exe = $cb->link_executable(objects => $obj, exe_file => "try");
+ unlink $obj;
+ $exe;
+ };
+ $self->notes($test, $exe ? 1 : 0);
+ unlink $exe if $exe;
+ unlink "try.c";
+ }
+}
+
+
sub note_time_limits {
my $self = shift;
my $source = "check_max.c";
- my $exe = "check_max";
+ my $exe = $self->notes("check_max") || "check_max";
unless( $self->up_to_date($source => $exe) ) {
warn "Building a program to test the range of your system time functions...\n";
my $cb = $self->cbuilder;
my $obj = $cb->compile(source => "check_max.c", include_dirs => ['y2038']);
$exe = $cb->link_executable(objects => $obj, exe_file => $exe);
+ $exe = $self->find_real_exe($exe);
+ $self->notes(check_max => $exe);
+ $self->add_to_cleanup($obj, $exe);
}
- $self->add_to_cleanup($exe);
-
- return if $self->up_to_date(["munge_config", $exe] => "y2038/time64_config.h");
+ return if $self->up_to_date(["y2038/time64_limits.h.in", "munge_config", $exe]
+ => "y2038/time64_limits.h");
warn " and running it...\n";
- my @maxes = `./$exe`;
+ my $json = `./$exe`;
+ $json =~ s{^\#.*\n}{}gm;
+ my $limits = from_json($json);
warn " Done.\n";
- my %limits;
- for my $line (@maxes) {
- next if $line =~ /^#/;
- my($key, $val) = split /\s+/, $line;
- next unless $key and $val;
- $limits{$key} = $val;
+ my %config;
+ for my $key (qw(gmtime localtime)) {
+ for my $limit (qw(min max)) {
+ $config{$key."_".$limit} = $limits->{$key}{$limit};
+ }
+ }
+
+ for my $key (qw(mktime timegm)) {
+ for my $limit (qw(min max)) {
+ my $struct = $limits->{$key}{$limit};
+ for my $tm (keys %$struct) {
+ $config{$key."_".$limit."_".$tm} = $struct->{$tm};
+ }
+ }
}
# Windows lies about being able to handle just a little bit of
# negative time.
for my $key (qw(gmtime_min localtime_min)) {
- if( -10_000 < $limits{$key} && $limits{$key} < 0 ) {
- $limits{$key} = 0;
+ if( -10_000 < $config{$key} && $config{$key} < 0 ) {
+ $config{$key} = 0;
}
}
- for my $key (sort { $a cmp $b } keys %limits) {
- my $val = $limits{$key};
- warn sprintf "%15s: %d\n", $key, $val;
- $self->notes($key, $limits{$key});
+ # OS X 10.6's gmtime is broken before -70546986201600
+ # See Apple bug 7654647
+ if( is_osx_106 ) {
+ for my $key (qw(gmtime_min localtime_min)) {
+ if( $config{$key} < -70546986201600 ) {
+ $config{$key} = -70546986201600;
+ }
+ }
+ }
+
+ for my $key (sort { $a cmp $b } keys %config) {
+ my $val = $config{$key};
+ warn sprintf "%15s: %s\n", $key, $val;
+ $self->notes($key, "$val");
}
return;
}
+# This is necessary because Cygwin nerotically puts a .exe on
+# every executable. This appears to be built into gcc.
+sub find_real_exe {
+ my $self = shift;
+ my $exe = shift;
+
+ my $real_exe;
+ for ($exe, "$exe.exe") {
+ $real_exe = $_ if -e;
+ }
+
+ warn "Can't find the executable, thought it was $exe"
+ unless defined $real_exe;
+
+ return $real_exe;
+}
+
+
sub ACTION_code {
my $self = shift;
- $self->note_time_limits;
+ $self->probe_system_time;
return $self->SUPER::ACTION_code(@_);
}
@@ -3,7 +3,7 @@ package Time::y2038::Everywhere;
use strict;
use warnings;
-our $VERSION = 20081111;
+our $VERSION = '20100218';
use Time::y2038;
@@ -6,7 +6,7 @@ use warnings;
use base qw(Exporter);
use XSLoader;
-our $VERSION = 20081111;
+our $VERSION = '20100218';
our @EXPORT = qw(localtime gmtime timegm timelocal);
XSLoader::load __PACKAGE__, $VERSION;
@@ -7,8 +7,9 @@ my $build = Module::Build->current;
my %config = (%Config, $build->notes);
+my $input_file = shift;
my $output_file = shift;
-my $input_file = $output_file . ".in";
+
open my $input_fh, $input_file or die "Can't open $input_file: $!";
open my $output_fh, ">$output_file" or die "Can't open $output_file: $!";
@@ -22,12 +23,7 @@ print $output_fh <<"END";
END
while(<$input_fh>) {
- my $matched = s{%%(.*)%%}{$config{$1}}g;
-
- if( $matched and !defined $config{$1} and !length $config{$1} ) {
- warn "Your configuration is missing \$config{$1}.\n".
- "Please edit $output_file by hand to fix.\n"
- }
+ my $matched = s{%%(.*)%%}{defined $config{$1} ? $config{$1} : ''}eg;
print $output_fh $_;
}
@@ -12,5 +12,3 @@ use Test::More 'no_plan';
is gmtime(2**52), "Sat Dec 6 03:48:16 142715360";
like localtime(2**52), qr/Dec .* 142715360/;
-
-
@@ -14,6 +14,32 @@ BEGIN {
local $ENV{TZ} = 'US/Pacific';
my $Test_Localtime = localtime(0) eq 'Wed Dec 31 16:00:00 1969';
+# Compare local time +/2 hours. Ignore seconds, minutes and dst
+my $Epsilon = [undef, undef, 2, 0, 0, 0, 0, 0, undef];
+
+sub date_ok {
+ my($have_date, $want_date, $epsilon, $name) = @_;
+
+ my $nok = 0;
+ for my $idx (0..$#{$want_date}) {
+ my $have = $have_date->[$idx];
+ my $want = $want_date->[$idx];
+ my $ep = $epsilon->[$idx];
+ next unless defined $ep;
+
+ $nok ||= abs($have - $want) > $epsilon;
+ }
+
+ ok( !$nok, $name );
+ if( $nok ) {
+ diag sprintf <<END, explain $have_date, explain $want_date;
+have: %s
+want: %s
+END
+ }
+
+ return $nok;
+}
# Test that we match the core's results inside the safe range.
{
@@ -42,13 +68,56 @@ my $Test_Localtime = localtime(0) eq 'Wed Dec 31 16:00:00 1969';
SKIP: {
skip "localtime() tests specific to US/Pacific time zone", 6 unless $Test_Localtime;
- is_deeply( [localtime(2**52)], [16, 48, 19, 5, 11, 142713460, 5, 339, 0], 'localtime(2**52)' );
- is_deeply( [localtime(-2**52)], [44, 11, 12, 25, 0, -142713321, 1, 24, 0], 'localtime(-2**52)' );
+ date_ok( [localtime(2**52)], [16, 48, 19, 5, 11, 142713460, 5, 339, 0],
+ $Epsilon, 'localtime(2**52)'
+ );
+ date_ok( [localtime(-2**52)], [44, 11, 12, 25, 0, -142713321, 1, 24, 0],
+ $Epsilon, 'localtime(-2**52)'
+ );
is_deeply( [localtime(1224479316)], [36, 8, 22, 19, 9, 108, 0, 292, 1], 'localtime() w/dst' );
- is( localtime(2**52), 'Fri Dec 5 19:48:16 142715360' );
- is( localtime(-2**52), 'Mon Jan 25 12:11:44 -142711421' );
- is( localtime(1224479316), 'Sun Oct 19 22:08:36 2008' );
+ # This is inverted because hash keys get stringified and the
+ # big numbers may lose accuracy.
+ my %times = (
+ 'Fri Dec 5 .* 142715360' => 2**52,
+ 'Mon Jun 19 .* 71358665' => 2**51,
+ 'Tue Sep 25 .* 35680317' => 2**50,
+ 'Mon Oct 25 .* 3058' => 2**35,
+ 'Fri Mar 7 .* 881' => -2**35,
+ 'Thu Apr 7 .* -35676378' => -2**50,
+ 'Sat Jul 14 .* -71354726' => -2**51,
+ 'Mon Jan 25 .* -142711421' => -2**52,
+ 'Sun Oct 19 22:08:36 2008' => 1224479316,
+ );
+ for my $want (keys %times) {
+ my $time = $times{$want};
+ like localtime($time), qr/$want/, sprintf "localtime(%.0f)", $time;
+ }
+}
+
+
+# Some sanity tests for the far, far future and far, far past
+{
+ my %time2year = (
+ -2**62 => -146138510344,
+ -2**52 => -142711421,
+ -2**48 => -8917617,
+ -2**46 => -2227927,
+ 2**46 => 2231866,
+ 2**48 => 8921556,
+ 2**52 => 142715360,
+ 2**62 => 146138514283
+ );
+
+ for my $time (sort keys %time2year) {
+ my $want = $time2year{$time};
+
+ my $have = (gmtime($time))[5] + 1900;
+ is $have, $want, "year check, gmtime($time)";
+
+ $have = (localtime($time))[5] + 1900;
+ is $have, $want, "year check, localtime($time)";
+ }
}
@@ -59,12 +128,12 @@ for my $name (qw(gmtime localtime)) {
};
# Test in void context
-#line 18
+#line 132
warning_like {
1;
$func->(0);
1;
- } qr/^\QUseless use of $name() in void context at $0 line 20.\E$/,
+ } qr/^\QUseless use of $name() in void context at $0 line 134.\E$/,
"void context warning";
@@ -79,13 +148,13 @@ for my $name (qw(gmtime localtime)) {
# Test too big or small a time.
my $huge_time = sprintf "%.0f", 2**65;
-#line 58
+#line 152
warning_like {
is $func->($huge_time), undef;
- } qr/^\Q$name($huge_time) can not be represented at $0 line 59\E/;
+ } qr/^\Q$name($huge_time) can not be represented at $0 line 153\E/;
-#line 63
+#line 157
warning_like {
is $func->(-$huge_time), undef;
- } qr/^\Q$name(-$huge_time) can not be represented at $0 line 64\E/;
+ } qr/^\Q$name(-$huge_time) can not be represented at $0 line 158\E/;
}
@@ -46,12 +46,103 @@ gmtime64_r() is a 64-bit equivalent of gmtime_r().
#include <time.h>
#include <errno.h>
#include "time64.h"
+#include "time64_limits.h"
+
+struct tm SYSTEM_MKTIME_MAX = {
+SYSTEM_MKTIME_MAX_TM_SEC,
+SYSTEM_MKTIME_MAX_TM_MIN,
+SYSTEM_MKTIME_MAX_TM_HOUR,
+SYSTEM_MKTIME_MAX_TM_MDAY,
+SYSTEM_MKTIME_MAX_TM_MON,
+SYSTEM_MKTIME_MAX_TM_YEAR,
+SYSTEM_MKTIME_MAX_TM_WDAY,
+SYSTEM_MKTIME_MAX_TM_YDAY,
+SYSTEM_MKTIME_MAX_TM_ISDST
+#ifdef HAS_TM_TM_GMTOFF
+,SYSTEM_MKTIME_MAX_TM_GMTOFF
+#else
+,0
+#endif
+#ifdef HAS_TM_TM_ZONE
+,SYSTEM_MKTIME_MAX_TM_ZONE
+#else
+,""
+#endif
+};
+
+const struct tm SYSTEM_MKTIME_MIN = {
+SYSTEM_MKTIME_MIN_TM_SEC,
+SYSTEM_MKTIME_MIN_TM_MIN,
+SYSTEM_MKTIME_MIN_TM_HOUR,
+SYSTEM_MKTIME_MIN_TM_MDAY,
+SYSTEM_MKTIME_MIN_TM_MON,
+SYSTEM_MKTIME_MIN_TM_YEAR,
+SYSTEM_MKTIME_MIN_TM_WDAY,
+SYSTEM_MKTIME_MIN_TM_YDAY,
+SYSTEM_MKTIME_MIN_TM_ISDST
+#ifdef HAS_TM_TM_GMTOFF
+,SYSTEM_MKTIME_MIN_TM_GMTOFF
+#else
+,0
+#endif
+#ifdef HAS_TM_TM_ZONE
+,SYSTEM_MKTIME_MIN_TM_ZONE
+#else
+,""
+#endif
+};
+
+#ifdef HAS_TIMEGM
+const struct tm SYSTEM_TIMEGM_MAX = {
+SYSTEM_TIMEGM_MAX_TM_SEC,
+SYSTEM_TIMEGM_MAX_TM_MIN,
+SYSTEM_TIMEGM_MAX_TM_HOUR,
+SYSTEM_TIMEGM_MAX_TM_MDAY,
+SYSTEM_TIMEGM_MAX_TM_MON,
+SYSTEM_TIMEGM_MAX_TM_YEAR,
+SYSTEM_TIMEGM_MAX_TM_WDAY,
+SYSTEM_TIMEGM_MAX_TM_YDAY,
+SYSTEM_TIMEGM_MAX_TM_ISDST
+#ifdef HAS_TM_TM_GMTOFF
+,SYSTEM_TIMEGM_MAX_TM_GMTOFF
+#else
+,0
+#endif
+#ifdef HAS_TM_TM_ZONE
+,SYSTEM_TIMEGM_MAX_TM_ZONE
+#else
+,""
+#endif
+};
+
+const struct tm SYSTEM_TIMEGM_MIN = {
+SYSTEM_TIMEGM_MIN_TM_SEC,
+SYSTEM_TIMEGM_MIN_TM_MIN,
+SYSTEM_TIMEGM_MIN_TM_HOUR,
+SYSTEM_TIMEGM_MIN_TM_MDAY,
+SYSTEM_TIMEGM_MIN_TM_MON,
+SYSTEM_TIMEGM_MIN_TM_YEAR,
+SYSTEM_TIMEGM_MIN_TM_WDAY,
+SYSTEM_TIMEGM_MIN_TM_YDAY,
+SYSTEM_TIMEGM_MIN_TM_ISDST
+#ifdef HAS_TM_TM_GMTOFF
+,SYSTEM_TIMEGM_MIN_TM_GMTOFF
+#else
+,0
+#endif
+#ifdef HAS_TM_TM_ZONE
+,SYSTEM_TIMEGM_MIN_TM_ZONE
+#else
+,""
+#endif
+};
+#endif /* HAS_TIMEGM */
/* Spec says except for stftime() and the _r() functions, these
all return static memory. Stabbings! */
static struct TM Static_Return_Date;
-/* static char Static_Return_String[1024]; */
+static char Static_Return_String[35];
static const int days_in_month[2][12] = {
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
@@ -63,6 +154,15 @@ static const int julian_days_by_month[2][12] = {
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335},
};
+static char wday_name[7][3] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+static char mon_name[12][3] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
static const int length_of_year[2] = { 365, 366 };
/* Some numbers relating to the gregorian cycle */
@@ -118,8 +218,8 @@ static const int dow_year_start[SOLAR_CYCLE_LENGTH] = {
#define CHEAT_DAYS (1199145600 / 24 / 60 / 60)
#define CHEAT_YEARS 108
-#define IS_LEAP(n) ((!(((n) + 1900) % 400) || (!(((n) + 1900) % 4) && (((n) + 1900) % 100))) != 0)
-#define WRAP(a,b,m) ((a) = ((a) < 0 ) ? ((b)--, (a) + (m)) : (a))
+#define IS_LEAP(n) ((!(((n) + 1900) % 400) || (!(((n) + 1900) % 4) && (((n) + 1900) % 100))) != 0)
+#define WRAP(a,b,m) ((a) = ((a) < 0 ) ? ((b)--, (a) + (m)) : (a))
#ifdef USE_SYSTEM_LOCALTIME
# define SHOULD_USE_SYSTEM_LOCALTIME(a) ( \
@@ -141,27 +241,80 @@ static const int dow_year_start[SOLAR_CYCLE_LENGTH] = {
/* Multi varadic macros are a C99 thing, alas */
#ifdef TIME_64_DEBUG
-# define TRACE(format) (fprintf(stderr, format))
-# define TRACE1(format, var1) (fprintf(stderr, format, var1))
-# define TRACE2(format, var1, var2) (fprintf(stderr, format, var1, var2))
-# define TRACE3(format, var1, var2, var3) (fprintf(stderr, format, var1, var2, var3))
+# define TIME64_TRACE(format) (fprintf(stderr, format))
+# define TIME64_TRACE1(format, var1) (fprintf(stderr, format, var1))
+# define TIME64_TRACE2(format, var1, var2) (fprintf(stderr, format, var1, var2))
+# define TIME64_TRACE3(format, var1, var2, var3) (fprintf(stderr, format, var1, var2, var3))
#else
-# define TRACE(format) ((void)0)
-# define TRACE1(format, var1) ((void)0)
-# define TRACE2(format, var1, var2) ((void)0)
-# define TRACE3(format, var1, var2, var3) ((void)0)
+# define TIME64_TRACE(format) ((void)0)
+# define TIME64_TRACE1(format, var1) ((void)0)
+# define TIME64_TRACE2(format, var1, var2) ((void)0)
+# define TIME64_TRACE3(format, var1, var2, var3) ((void)0)
#endif
static int is_exception_century(Year year)
{
int is_exception = ((year % 100 == 0) && !(year % 400 == 0));
- TRACE1("# is_exception_century: %s\n", is_exception ? "yes" : "no");
+ TIME64_TRACE1("# is_exception_century: %s\n", is_exception ? "yes" : "no");
return(is_exception);
}
+/* Compare two dates.
+ The result is like cmp.
+ Ignores things like gmtoffset and dst
+*/
+int cmp_date( const struct TM* left, const struct tm* right ) {
+ if( left->tm_year > right->tm_year )
+ return 1;
+ else if( left->tm_year < right->tm_year )
+ return -1;
+
+ if( left->tm_mon > right->tm_mon )
+ return 1;
+ else if( left->tm_mon < right->tm_mon )
+ return -1;
+
+ if( left->tm_mday > right->tm_mday )
+ return 1;
+ else if( left->tm_mday < right->tm_mday )
+ return -1;
+
+ if( left->tm_hour > right->tm_hour )
+ return 1;
+ else if( left->tm_hour < right->tm_hour )
+ return -1;
+
+ if( left->tm_min > right->tm_min )
+ return 1;
+ else if( left->tm_min < right->tm_min )
+ return -1;
+
+ if( left->tm_sec > right->tm_sec )
+ return 1;
+ else if( left->tm_sec < right->tm_sec )
+ return -1;
+
+ return 0;
+}
+
+
+/* Check if a date is safely inside a range.
+ The intention is to check if its a few days inside.
+*/
+int date_in_safe_range( const struct TM* date, const struct tm* min, const struct tm* max ) {
+ if( cmp_date(date, min) == -1 )
+ return 0;
+
+ if( cmp_date(date, max) == 1 )
+ return 0;
+
+ return 1;
+}
+
+
/* timegm() is not in the C or POSIX spec, but it is such a useful
extension I would be remiss in leaving it out. Also I need it
for localtime64()
@@ -183,7 +336,7 @@ Time64_T timegm64(const struct TM *date) {
orig_year -= cycles * 400;
days += (Time64_T)cycles * days_in_gregorian_cycle;
}
- TRACE3("# timegm/ cycles: %d, days: %lld, orig_year: %lld\n", cycles, days, orig_year);
+ TIME64_TRACE3("# timegm/ cycles: %d, days: %lld, orig_year: %lld\n", cycles, days, orig_year);
if( orig_year > 70 ) {
year = 70;
@@ -261,7 +414,7 @@ static Year cycle_offset(Year year)
exceptions = year_diff / 100;
exceptions -= year_diff / 400;
- TRACE3("# year: %lld, exceptions: %lld, year_diff: %lld\n",
+ TIME64_TRACE3("# year: %lld, exceptions: %lld, year_diff: %lld\n",
year, exceptions, year_diff);
return exceptions * 16;
@@ -320,7 +473,7 @@ static int safe_year(const Year year)
else
assert(0);
- TRACE3("# year: %lld, year_cycle: %lld, safe_year: %d\n",
+ TIME64_TRACE3("# year: %lld, year_cycle: %lld, safe_year: %d\n",
year, year_cycle, safe_year);
assert(safe_year <= MAX_SAFE_YEAR && safe_year >= MIN_SAFE_YEAR);
@@ -329,7 +482,7 @@ static int safe_year(const Year year)
}
-void copy_tm_to_TM(const struct tm *src, struct TM *dest) {
+void copy_tm_to_TM64(const struct tm *src, struct TM *dest) {
if( src == NULL ) {
memset(dest, 0, sizeof(*dest));
}
@@ -361,7 +514,7 @@ void copy_tm_to_TM(const struct tm *src, struct TM *dest) {
}
-void copy_TM_to_tm(const struct TM *src, struct tm *dest) {
+void copy_TM64_to_tm(const struct TM *src, struct tm *dest) {
if( src == NULL ) {
memset(dest, 0, sizeof(*dest));
}
@@ -394,8 +547,8 @@ void copy_TM_to_tm(const struct TM *src, struct tm *dest) {
/* Simulate localtime_r() to the best of our ability */
-struct tm * fake_localtime_r(const time_t *clock, struct tm *result) {
- const struct tm *static_result = localtime(clock);
+struct tm * fake_localtime_r(const time_t *time, struct tm *result) {
+ const struct tm *static_result = localtime(time);
assert(result != NULL);
@@ -411,8 +564,8 @@ struct tm * fake_localtime_r(const time_t *clock, struct tm *result) {
/* Simulate gmtime_r() to the best of our ability */
-struct tm * fake_gmtime_r(const time_t *clock, struct tm *result) {
- const struct tm *static_result = gmtime(clock);
+struct tm * fake_gmtime_r(const time_t *time, struct tm *result) {
+ const struct tm *static_result = gmtime(time);
assert(result != NULL);
@@ -458,15 +611,17 @@ Time64_T mktime64(const struct TM *input_date) {
Time64_T time;
Year year = input_date->tm_year + 1900;
- if( MIN_SAFE_YEAR <= year && year <= MAX_SAFE_YEAR ) {
- copy_TM_to_tm(input_date, &safe_date);
+ if( date_in_safe_range(input_date, &SYSTEM_MKTIME_MIN, &SYSTEM_MKTIME_MAX) )
+ {
+ printf("Using system mktime\n");
+ copy_TM64_to_tm(input_date, &safe_date);
return (Time64_T)mktime(&safe_date);
}
/* Have to make the year safe in date else it won't fit in safe_date */
date = *input_date;
date.tm_year = safe_year(year) - 1900;
- copy_TM_to_tm(&date, &safe_date);
+ copy_TM64_to_tm(&date, &safe_date);
time = (Time64_T)mktime(&safe_date);
@@ -496,11 +651,11 @@ struct TM *gmtime64_r (const Time64_T *in_time, struct TM *p)
/* Use the system gmtime() if time_t is small enough */
if( SHOULD_USE_SYSTEM_GMTIME(*in_time) ) {
- time_t safe_time = *in_time;
+ time_t safe_time = (time_t)*in_time;
struct tm safe_date;
GMTIME_R(&safe_time, &safe_date);
- copy_tm_to_TM(&safe_date, p);
+ copy_tm_to_TM64(&safe_date, p);
assert(check_tm(p));
return p;
@@ -621,20 +776,20 @@ struct TM *localtime64_r (const Time64_T *time, struct TM *local_tm)
/* Use the system localtime() if time_t is small enough */
if( SHOULD_USE_SYSTEM_LOCALTIME(*time) ) {
- safe_time = *time;
+ safe_time = (time_t)*time;
- TRACE1("Using system localtime for %lld\n", *time);
+ TIME64_TRACE1("Using system localtime for %lld\n", *time);
LOCALTIME_R(&safe_time, &safe_date);
- copy_tm_to_TM(&safe_date, local_tm);
+ copy_tm_to_TM64(&safe_date, local_tm);
assert(check_tm(local_tm));
return local_tm;
}
if( gmtime64_r(time, &gm_tm) == NULL ) {
- TRACE1("gmtime64_r returned null for %lld\n", *time);
+ TIME64_TRACE1("gmtime64_r returned null for %lld\n", *time);
return NULL;
}
@@ -644,21 +799,21 @@ struct TM *localtime64_r (const Time64_T *time, struct TM *local_tm)
gm_tm.tm_year < (1970 - 1900)
)
{
- TRACE1("Mapping tm_year %lld to safe_year\n", (Year)gm_tm.tm_year);
+ TIME64_TRACE1("Mapping tm_year %lld to safe_year\n", (Year)gm_tm.tm_year);
gm_tm.tm_year = safe_year((Year)(gm_tm.tm_year + 1900)) - 1900;
}
- safe_time = timegm64(&gm_tm);
+ safe_time = (time_t)timegm64(&gm_tm);
if( LOCALTIME_R(&safe_time, &safe_date) == NULL ) {
- TRACE1("localtime_r(%d) returned NULL\n", (int)safe_time);
+ TIME64_TRACE1("localtime_r(%d) returned NULL\n", (int)safe_time);
return NULL;
}
- copy_tm_to_TM(&safe_date, local_tm);
+ copy_tm_to_TM64(&safe_date, local_tm);
local_tm->tm_year = orig_year;
if( local_tm->tm_year != orig_year ) {
- TRACE2("tm_year overflow: tm_year %lld, orig_year %lld\n",
+ TIME64_TRACE2("tm_year overflow: tm_year %lld, orig_year %lld\n",
(Year)local_tm->tm_year, (Year)orig_year);
#ifdef EOVERFLOW
@@ -699,11 +854,59 @@ struct TM *localtime64_r (const Time64_T *time, struct TM *local_tm)
}
+int valid_tm_wday( const struct TM* date ) {
+ if( 0 <= date->tm_wday && date->tm_wday <= 6 )
+ return 1;
+ else
+ return 0;
+}
+
+int valid_tm_mon( const struct TM* date ) {
+ if( 0 <= date->tm_mon && date->tm_mon <= 11 )
+ return 1;
+ else
+ return 0;
+}
+
+
+char *asctime64_r( const struct TM* date, char *result ) {
+ /* I figure everything else can be displayed, even hour 25, but if
+ these are out of range we walk off the name arrays */
+ if( !valid_tm_wday(date) || !valid_tm_mon(date) )
+ return NULL;
+
+ sprintf(result, TM64_ASCTIME_FORMAT,
+ wday_name[date->tm_wday],
+ mon_name[date->tm_mon],
+ date->tm_mday, date->tm_hour,
+ date->tm_min, date->tm_sec,
+ 1900 + date->tm_year);
+
+ return result;
+}
+
+
+char *ctime64_r( const Time64_T* time, char* result ) {
+ struct TM date;
+
+ localtime64_r( time, &date );
+ return asctime64_r( &date, result );
+}
+
+
+/* Non-thread safe versions of the above */
struct TM *localtime64(const Time64_T *time) {
return localtime64_r(time, &Static_Return_Date);
}
-
struct TM *gmtime64(const Time64_T *time) {
return gmtime64_r(time, &Static_Return_Date);
}
+
+char *asctime64( const struct TM* date ) {
+ return asctime64_r( date, Static_Return_String );
+}
+
+char *ctime64( const Time64_T* time ) {
+ return asctime64(localtime64(time));
+}
@@ -1,9 +1,8 @@
-#include <time.h>
-#include "time64_config.h"
-
#ifndef TIME64_H
# define TIME64_H
+#include <time.h>
+#include "time64_config.h"
/* Set our custom types */
typedef INT_64_T Int64;
@@ -44,10 +43,15 @@ struct TM64 {
/* Declare public functions */
struct TM *gmtime64_r (const Time64_T *, struct TM *);
struct TM *localtime64_r (const Time64_T *, struct TM *);
-
struct TM *gmtime64 (const Time64_T *);
struct TM *localtime64 (const Time64_T *);
+char *asctime64 (const struct TM *);
+char *asctime64_r (const struct TM *, char *);
+
+char *ctime64 (const Time64_T*);
+char *ctime64_r (const Time64_T*, char*);
+
Time64_T timegm64 (const struct TM *);
Time64_T mktime64 (const struct TM *);
Time64_T timelocal64 (const struct TM *);
@@ -65,4 +69,13 @@ Time64_T timelocal64 (const struct TM *);
# define GMTIME_R(clock, result) fake_gmtime_r(clock, result)
#endif
+
+/* Use a different asctime format depending on how big the year is */
+#ifdef USE_TM64
+ #define TM64_ASCTIME_FORMAT "%.3s %.3s%3d %.2d:%.2d:%.2d %lld\n"
+#else
+ #define TM64_ASCTIME_FORMAT "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n"
+#endif
+
+
#endif
@@ -28,6 +28,12 @@
*/
#define USE_TM64
+#ifdef USE_TM64
+ #define TM64_ASCTIME_FORMAT "%.3s %.3s%3d %.2d:%.2d:%.2d %lld\n"
+#else
+ #define TM64_ASCTIME_FORMAT "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n"
+#endif
+
/* Availability of system functions.
@@ -40,7 +46,15 @@
HAS_TIMEGM
Define if your system has timegm(), a GNU extension.
*/
-/* Handled by config */
+#if %%HAS_GMTIME_R%%
+#define HAS_GMTIME_R
+#endif
+#if %%HAS_LOCALTIME_R%%
+#define HAS_LOCALTIME_R
+#endif
+#if %%HAS_TIMEGM%%
+#define HAS_TIMEGM
+#endif
/* Details of non-standard tm struct elements.
@@ -53,31 +67,25 @@
True if your tm struct has a "tm_zone" element.
A BSD extension.
*/
-/* #define HAS_TM_TM_GMTOFF */
-/* #define HAS_TM_TM_ZONE */
+#if %%HAS_TM_TM_GMTOFF%%
+#define HAS_TM_TM_GMTOFF
+#endif
+#if %%HAS_TM_TM_ZONE%%
+#define HAS_TM_TM_ZONE
+#endif
/* USE_SYSTEM_LOCALTIME
USE_SYSTEM_GMTIME
+ USE_SYSTEM_MKTIME
+ USE_SYSTEM_TIMEGM
Should we use the system functions if the time is inside their range?
Your system localtime() is probably more accurate, but our gmtime() is
fast and safe.
*/
#define USE_SYSTEM_LOCALTIME
/* #define USE_SYSTEM_GMTIME */
-
-
-/* SYSTEM_LOCALTIME_MAX
- SYSTEM_LOCALTIME_MIN
- SYSTEM_GMTIME_MAX
- SYSTEM_GMTIME_MIN
- Maximum and minimum values your system's gmtime() and localtime()
- can handle. We will use your system functions if the time falls
- inside these ranges.
-*/
-#define SYSTEM_LOCALTIME_MAX %%localtime_max%%
-#define SYSTEM_LOCALTIME_MIN %%localtime_min%%
-#define SYSTEM_GMTIME_MAX %%gmtime_max%%
-#define SYSTEM_GMTIME_MIN %%gmtime_min%%
+#define USE_SYSTEM_MKTIME
+/* #define USE_SYSTEM_TIMEGM */
#endif /* TIME64_CONFIG_H */
@@ -0,0 +1,88 @@
+/*
+ Maximum and minimum inputs your system's respective time functions
+ can correctly handle. time64.h will use your system functions if
+ the input falls inside these ranges and corresponding USE_SYSTEM_*
+ constant is defined.
+*/
+
+#ifndef TIME64_LIMITS_H
+#define TIME64_LIMITS_H
+
+/* Max/min for localtime() */
+#define SYSTEM_LOCALTIME_MAX %%localtime_max%%
+#define SYSTEM_LOCALTIME_MIN %%localtime_min%%
+
+/* Max/min for gmtime() */
+#define SYSTEM_GMTIME_MAX %%gmtime_max%%
+#define SYSTEM_GMTIME_MIN %%gmtime_min%%
+
+/* Max/min for mktime() */
+#define SYSTEM_MKTIME_MAX_TM_SEC %%mktime_max_tm_sec%%
+#define SYSTEM_MKTIME_MAX_TM_MIN %%mktime_max_tm_min%%
+#define SYSTEM_MKTIME_MAX_TM_HOUR %%mktime_max_tm_hour%%
+#define SYSTEM_MKTIME_MAX_TM_MDAY %%mktime_max_tm_mday%%
+#define SYSTEM_MKTIME_MAX_TM_MON %%mktime_max_tm_mon%%
+#define SYSTEM_MKTIME_MAX_TM_YEAR %%mktime_max_tm_year%%
+#define SYSTEM_MKTIME_MAX_TM_WDAY %%mktime_max_tm_wday%%
+#define SYSTEM_MKTIME_MAX_TM_YDAY %%mktime_max_tm_yday%%
+#define SYSTEM_MKTIME_MAX_TM_ISDST %%mktime_max_tm_isdst%%
+#ifdef HAS_TM_TM_ZONE
+ #define SYSTEM_MKTIME_MAX_TM_ZONE "%%mktime_max_tm_zone%%"
+#endif
+#ifdef HAS_TM_TM_GMTOFF
+ #define SYSTEM_MKTIME_MAX_TM_GMTOFF %%mktime_max_tm_gmtoff%%
+#endif
+
+#define SYSTEM_MKTIME_MIN_TM_SEC %%mktime_min_tm_sec%%
+#define SYSTEM_MKTIME_MIN_TM_MIN %%mktime_min_tm_min%%
+#define SYSTEM_MKTIME_MIN_TM_HOUR %%mktime_min_tm_hour%%
+#define SYSTEM_MKTIME_MIN_TM_MDAY %%mktime_min_tm_mday%%
+#define SYSTEM_MKTIME_MIN_TM_MON %%mktime_min_tm_mon%%
+#define SYSTEM_MKTIME_MIN_TM_YEAR %%mktime_min_tm_year%%
+#define SYSTEM_MKTIME_MIN_TM_WDAY %%mktime_min_tm_wday%%
+#define SYSTEM_MKTIME_MIN_TM_YDAY %%mktime_min_tm_yday%%
+#define SYSTEM_MKTIME_MIN_TM_ISDST %%mktime_min_tm_isdst%%
+#ifdef HAS_TM_TM_ZONE
+ #define SYSTEM_MKTIME_MIN_TM_ZONE "%%mktime_min_tm_zone%%"
+#endif
+#ifdef HAS_TM_TM_GMTOFF
+ #define SYSTEM_MKTIME_MIN_TM_GMTOFF %%mktime_min_tm_gmtoff%%
+#endif
+
+
+/* Max/min for timegm() */
+#ifdef HAS_TIMEGM
+ #define SYSTEM_TIMEGM_MAX_TM_SEC %%timegm_max_tm_sec%%
+ #define SYSTEM_TIMEGM_MAX_TM_MIN %%timegm_max_tm_min%%
+ #define SYSTEM_TIMEGM_MAX_TM_HOUR %%timegm_max_tm_hour%%
+ #define SYSTEM_TIMEGM_MAX_TM_MDAY %%timegm_max_tm_mday%%
+ #define SYSTEM_TIMEGM_MAX_TM_MON %%timegm_max_tm_mon%%
+ #define SYSTEM_TIMEGM_MAX_TM_YEAR %%timegm_max_tm_year%%
+ #define SYSTEM_TIMEGM_MAX_TM_WDAY %%timegm_max_tm_wday%%
+ #define SYSTEM_TIMEGM_MAX_TM_YDAY %%timegm_max_tm_yday%%
+ #define SYSTEM_TIMEGM_MAX_TM_ISDST %%timegm_max_tm_isdst%%
+ #ifdef HAS_TM_TM_ZONE
+ #define SYSTEM_TIMEGM_MAX_TM_ZONE "%%timegm_max_tm_zone%%"
+ #endif
+ #ifdef HAS_TM_TM_GMTOFF
+ #define SYSTEM_TIMEGM_MAX_TM_GMTOFF %%timegm_max_tm_gmtoff%%
+ #endif
+
+ #define SYSTEM_TIMEGM_MIN_TM_SEC %%timegm_min_tm_sec%%
+ #define SYSTEM_TIMEGM_MIN_TM_MIN %%timegm_min_tm_min%%
+ #define SYSTEM_TIMEGM_MIN_TM_HOUR %%timegm_min_tm_hour%%
+ #define SYSTEM_TIMEGM_MIN_TM_MDAY %%timegm_min_tm_mday%%
+ #define SYSTEM_TIMEGM_MIN_TM_MON %%timegm_min_tm_mon%%
+ #define SYSTEM_TIMEGM_MIN_TM_YEAR %%timegm_min_tm_year%%
+ #define SYSTEM_TIMEGM_MIN_TM_WDAY %%timegm_min_tm_wday%%
+ #define SYSTEM_TIMEGM_MIN_TM_YDAY %%timegm_min_tm_yday%%
+ #define SYSTEM_TIMEGM_MIN_TM_ISDST %%timegm_min_tm_isdst%%
+ #ifdef HAS_TM_TM_ZONE
+ #define SYSTEM_TIMEGM_MIN_TM_ZONE "%%timegm_min_tm_zone%%"
+ #endif
+ #ifdef HAS_TM_TM_GMTOFF
+ #define SYSTEM_TIMEGM_MIN_TM_GMTOFF %%timegm_min_tm_gmtoff%%
+ #endif
+#endif
+
+#endif /* TIME64_LIMITS_H */